// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: leon

#pragma once

#include <memory>
#include <string>
#include <vector>

#include "coin2/exchange/base/symbology/product.h"
#include "coin2/exchange/base/symbology/util.h"

namespace coin2::exchange::base::symbology {

template <typename CurrencyT, typename ProductT>
class AmmProduct : public CurrencyPairProduct<CurrencyT> {
 public:
  AmmProduct(
      const CurrencyT& base,
      const CurrencyT& quote,
      int32_t fee_tier,
      const std::string& order_native_symbol,
      const std::string& feed_native_symbol,
      const ExchangeType exchange,
      bool omit_fee)
      : CurrencyPairProduct<CurrencyT>(
            base,
            quote,
            ConvertToAmmNormStr(base, quote, fee_tier, omit_fee),
            order_native_symbol,
            ConvertToAmmNormStr(base, quote, fee_tier, omit_fee),
            feed_native_symbol,
            MarketType::Spot,
            exchange),
        fee_tier_(fee_tier),
        omit_fee_(omit_fee) {}

 public:
  static std::unique_ptr<ProductT> FromNormStrImpl(
      const std::string& product_str,
      const int64_t resolve_timestamp) {
    std::string base_str;
    std::string quote_str;
    int32_t fee_tier;
    bool omit_fee;
    bool success = ParseAmmNormStr(
        product_str, &base_str, &quote_str, &fee_tier, &omit_fee);
    if (success) {
      auto base = CurrencyT::FromNormStrNoThrow(base_str, resolve_timestamp);
      auto quote = CurrencyT::FromNormStrNoThrow(quote_str, resolve_timestamp);
      if (base && quote) {
        return std::make_unique<ProductT>(*base, *quote, fee_tier, omit_fee);
      } else {
        return {};
      }
    } else {
      return {};
    }
  }

  IProduct* Clone() const final {
    auto* prod = new ProductT(
        CurrencyPairProduct<CurrencyT>::base_impl(),
        CurrencyPairProduct<CurrencyT>::quote_impl(),
        fee_tier_,
        omit_fee_);
    prod->resolve_timestamp_ = this->resolve_timestamp();
    return prod;
  }

  int32_t fee_tier() const { return fee_tier_; }

 private:
  static bool
  ParseAmmNormStr(
      const std::string currency_pair,
      std::string* base,
      std::string* quote,
      int32_t* fee_tier,
      bool* omit_fee) {
    std::vector<std::string> out;
    out = Split(currency_pair, "-.");
    if (out.size() < 2) {
      return false;
    }
    *base = out[0];
    *quote = out[1];
    *fee_tier = 30;
    *omit_fee = true;
    if (out.size() == 3) {
      std::size_t pos{};
      *fee_tier = std::stoi(out[2], &pos);
      CHECK_EQ(out[2].substr(pos), "bp");
      *omit_fee = false;
    }
    return true;
  }

  static std::string ConvertToAmmNormStr(
      const ICurrency& base,
      const ICurrency& quote,
      int32_t fee_tier,
      bool omit_fee) {
    std::stringstream ss;
    ss << base.currency() << "-" << quote.currency();
    if (!omit_fee) {
       ss << "." << fee_tier << "bp";
    }
    return ss.str();
  }

 private:
  const int32_t fee_tier_;
  const bool omit_fee_;
};

}  // namespace coin2::exchange::base::symbology
